<!DOCTYPE html>
<html lang="zh-CN"
>
<head>
    <title>WebSocket聊天</title>
    <link href="/css/bootstrap4.min.css" rel="stylesheet" crossorigin="anonymous">
    <link rel="stylesheet" href="/css/animate.min.css">
    <link rel="stylesheet" href="/css/style.css">
    <script src="/js/jquery.js"></script>
    <script src="/js/sockjs.min.js"></script>
    <script src="/js/stomp.min.js"></script>
</head>
<body>

<div id="root">
    <!-- 头部导航 -->
    <nav class="navbar navbar-dark bg-primary">
        <div class="container">
            <a class="navbar-brand monospace" href="javascript:void(0)">
                <span>WebSocket</span>
                <small>在线测试工具</small>
            </a>
        </div>
    </nav>
    <!-- 主体内容 -->
    <div class="container mt-3 main-container">
        <div class="row monospace">
            <div class="col-sm-12">
                <div class="card">
                    <!-- 应用容器 -->
                    <div class="card-body">
                        <div class="row">
                            <!-- 服务设置 -->
                            <div class="col-sm-11 col-md-4 offset-md-1">
                                <h5 class="card-title" id="title_login" display="block">
                                    <input type="text" id="uname" value="一灰">
                                    <button class="btn btn-info" onclick="login()">登录</button>
                                </h5>
                                <h5 class="card-title" id="title_welcome" display="none">
                                    成功建立连接:
                                    <bold id="sname"></bold>
                                </h5>
                            </div>
                            <div class="col-sm-12 col-md-6">
                                <!-- 连接地址 -->
                                <div class="card-text">
                                    <div class="input-group">
                                        <div class="input-group-prepend">
                                            <div class="input-group-text">ws://localhost:8080</div>
                                        </div>
                                        <input type="text" class="form-control" id="channel"
                                               placeholder="输入 WebSocket 服务器地址" value="/ws/chat/channel">
                                        <div class="input-group-append">
                                            <button type="button" class="btn btn-block btn-success" id="connBtn"
                                                    onclick="connect(this)">连接
                                            </button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                        <div class="col-sm-12 col-md-12">
                            <hr class="divider divider-dashed">
                            <br/>
                        </div>
                        <div class="row">
                            <!-- 左侧面板 -->
                            <div class="col-sm-12 col-md-12">
                                <!-- 发包设置 -->
                                <div class="col-sm-12 mt-12">
                                    <h5 class="card-title">全局频道</h5>
                                    <hr class="divider divider-dashed">
                                    <!-- 自动发送 -->
                                    <div class="card-text">
                                        <div class="input-group">
                                            <div class="input-group-prepend">
                                                <div class="input-group-text">订阅</div>
                                            </div>
                                            <!-- 默认订阅的Broker -->
                                            <input value="globalChannel" type="text" class="form-control text-center"
                                                   id="topic1">
                                            <div class="input-group-append">
                                                <button type="button" class="btn btn-block btn-success"
                                                        onclick="subscribe(this, 'topic1', 'console-box')"> 开始订阅
                                                </button>
                                            </div>
                                        </div>
                                    </div>

                                </div>
                                <div class="row">
                                    <div class="col-sm-12 col-md-6">
                                        <!-- 手动发送 -->
                                        <div class="card-text mt-2">
                                            <div style="height: 300px;padding: 1em;border: 1px solid #dbd6d6;overflow-y: scroll;" id="chatContentArea" contenteditable>输入内容吧
                                            </div>
                                        </div>
                                        <div class="card-text mt-2">
                                            <button class="btn btn-block btn-success"
                                                    onclick="sendMsg('chatContentArea', 'topic1')">发送到服务端
                                            </button>
                                        </div>
                                    </div>
                                    <!-- 调试消息 -->
                                    <div class="col-sm-12 col-md-6">
                                        <h5 class="card-title" style="padding-top: 1em">聊天信息</h5>
                                        <hr class="divider divider-dashed">
                                        <div class="card-text">
                                            <div style="min-height:400px;background: #faf9f9;padding: 1em;"
                                                 id="console-box">
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
<script th:inline="javascript">
    // 监听粘贴事件，支持图片的传递
    document.querySelector('#chatContentArea').addEventListener('paste', function (e) {
        var cbd = e.clipboardData;
        console.log("粘贴事件触发:", cbd);
        var ua = window.navigator.userAgent;
        if (!(cbd && cbd.items)) {
            return;
        }
        //判断浏览器
        if (cbd.items && cbd.items.length === 2 && cbd.items[0].kind === 'string' && cbd.items[1].kind === 'file' && cbd.types && cbd.types.length === 2 && cbd.types[0] === 'text/plain' && cbd.typesp[1] === 'Files' && ua.match(/Macintosh/i) && Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49) {
            return;
        }
        //判断是图片渲染
        for (var i = 0; i < cbd.items.length; i++) {
            var item = cbd.items[i];
            if (item.kind == 'file') {
                var blob = item.getAsFile();
                if (blob.size === 0) {
                    return;
                }
                var reader = new FileReader();
                var imgs = new Image();
                imgs.file = blob;
                reader.onload = (function (aImg) {
                    return function (e) {
                        aImg.src = e.target.result;
                    }
                })(imgs)
                reader.readAsDataURL(blob)
                document.querySelector('#chatContentArea').appendChild(imgs)
            }
        }
    }, false);


    const loginUser = [[ ${uname}]];
    updateLoginInfo(loginUser);

    function login() {
        // 首先进行登录
        const uname = $("#uname").val();
        $.ajax('http://localhost:8080/login?name=' + uname, {
            method: 'GET',
            success: function (result) {
                console.log("登录成功", result);
                updateLoginInfo(uname);
            },
            error: function (err) {
                console.log(err);
            }
        })
    }

    function updateLoginInfo(uname) {
        console.log("当前登录用户: ", loginUser);
        if (uname) {
            // 已成功登录
            $("#sname").text(uname)
            $('#title_welcome').show()
            $('#title_login').hide()
        } else {
            // 未登录
            $('#title_welcome').hide()
            $('#title_login').show()
        }
    }

    let stompClient = null;
    let subscribeMap = {};

    /**
     * 建立ws连接
     */
    function connect(ref) {
        const channel = $('#channel').val();
        console.log("建立websocket连接: ", channel);
        if (ref.classList.contains('btn-success')) {
            // 建立连接
            ref.textContent = '中断';
            ref.classList.remove('btn-success');
            ref.classList.add('btn-danger');
            $('#channel').prop("disabled", true);

            const socket = new SockJS(channel);
            stompClient = Stomp.over(socket);
            stompClient.connect({"uname": $("#uname").val()}, function (frame) {
                console.log('Connected: ' + frame);
            }, {id: loginUser});
            socket.onclose = disconnect;
        } else {
            disconnect(ref);
        }
    }

    /**
     * 取消链接
     */
    function disconnect(ref) {
        if (stompClient !== null) {
            stompClient.disconnect();
            stompClient = null;
        }

        if (ref == null) {
            ref = $("#connBtn");
        }
        console.log("连接中断: ", ref);
        // 取消连接
        ref.textContent = '连接';
        ref.classList.add('btn-success');
        ref.classList.remove('btn-danger');
        $('#channel').prop("disabled", false);
    }

    /**
     * 订阅
     * @param ref
     * @param id
     */
    function subscribe(ref, id, showMsgId) {
        const channel = $(`#${id}`).val();
        console.log("准备订阅: ", channel);

        SUBS_ID = "/topic/chat/" + channel;

        if (ref.classList.contains('btn-success')) {
            if (stompClient == null) {
                alert("请先建立链接");
                return;
            }

            // 执行订阅
            ref.textContent = '订阅成功';
            ref.classList.remove('btn-success');
            ref.classList.add('btn-danger');
            $(`#${id}`).prop("disabled", true);

            // 订阅，并保存返回的对象，用户后续的取消订阅
            subscribeMap[channel] = stompClient.subscribe('/topic/chat/' + channel, function (greeting) {
                    // 表示这个长连接，订阅了 "/topic/hello" , 这样后端像这个路径转发消息时，我们就可以拿到对应的返回
                    console.log("resp: ", greeting.body)
                    showGreeting(showMsgId, greeting.body);
                }, {id: SUBS_ID}
            )
        } else {
            // 取消订阅
            ref.textContent = '开始订阅';
            ref.classList.add('btn-success');
            ref.classList.remove('btn-danger');
            $(`#${id}`).prop("disabled", false);
            // 下面这种取消订阅方式，和if中的取消订阅方式等价
            // stompClient.unsubscribe(SUBS_ID);
            if (subscribeMap[channel]) {
                subscribeMap[channel].unsubscribe();
                subscribeMap[channel] = null;
            }
        }
    }

    function showGreeting(showMsgId, txt) {
        const res = JSON.parse(txt);
        const user = res['uname'];
        function formatTimestamp(timestamp) {
            const date = new Date(timestamp);
            const year = date.getFullYear();
            const month = (date.getMonth() + 1).toString().padStart(2, '0');
            const day = date.getDate().toString().padStart(2, '0');
            const hours = date.getHours().toString().padStart(2, '0');
            const minutes = date.getMinutes().toString().padStart(2, '0');
            const seconds = date.getSeconds().toString().padStart(2, '0');

            return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
        }

        const time = formatTimestamp(res['time']);
        const msg = res['msg'];

        $(`#${showMsgId}`).prepend(`<div> <div style="color:lightgray"> ${user} @ ${time} </div> ${msg} </div>`)
    }

    function sendMsg(msgId, topicId) {
        // 表示将消息转发到哪个目标，类似与http请求中的path路径，对应的是后端 @MessageMapping 修饰的方法
        const channel = $(`#${topicId}`).val();
        const msg = $(`#${msgId}`).html();

        // 富文本传输，主要是通过base64的方式进行交互
        console.log("input msg:", channel, msg);
        stompClient.send('/app/msg/' + channel, {}, msg);
        console.log("消息发送完成!");
    }

</script>
</html>